package com.ebspp.client.serverManager;

import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.thrift.TException;
import org.apache.thrift.transport.TTransportException;

import com.ebspp.global.General;
import com.ebspp.thrift.DataMsg;
import com.ebspp.thrift.Identity;
import com.ebspp.thrift.ResourceType;

/**
 * It managers all the write connections
 * @author x23pan
 *
 */
public class WriteConnManager {
	
	int wPort;
	IStat stat;
	Map<Integer, Map<String, WriteConnection>> wConnMap;
	
	public WriteConnManager(IStat stat)
	{
		this.wPort = General.wPort;
		this.stat = stat;
		wConnMap = Collections.synchronizedMap(new HashMap<Integer, Map<String, WriteConnection>>());
	}
	

	public synchronized WriteConnection createWConn(String server, ResourceType resType, 
														int bufID, Identity clientID)
	{		
		if(wConnMap.get(bufID) == null)
			wConnMap.put(bufID, Collections.synchronizedMap(new HashMap<String, WriteConnection>()));
		Map<String, WriteConnection> serverWConn = wConnMap.get(bufID);
		if(serverWConn.get(server) != null)
			return serverWConn.get(server);
		
		
		WriteConnection wConn = null;
		try 
		{
			wConn = new WriteConnection(server, wPort, resType, clientID, stat);
			wConn.connect();
			
			serverWConn = wConnMap.get(bufID);
			serverWConn.put(server, wConn);
		} 
		catch (TTransportException e) {
			// TODO Auto-generated catch block
			System.err.println("Unable to init write connection to " + server + "\n" + e.getMessage());
			e.printStackTrace();
			if(serverWConn.size() == 0)
				wConnMap.remove(bufID);
			return null;
		}
		
		return wConn;
	}
	
	public synchronized boolean closeWConn(int bufID, String server)
	{
		if(wConnMap.get(bufID) == null)
			return true;
		Map<String, WriteConnection> serverWConn = wConnMap.get(bufID);
		if(serverWConn.get(server) == null)
			return true;
		
		WriteConnection wConn = serverWConn.get(server);
		wConn.close();
		
		serverWConn.remove(server);
		if(serverWConn.size() == 0)
			wConnMap.remove(bufID);
		
		return true;
	}
	
	public boolean provision(String server, int bufID, int amount)
	{
		if(wConnMap.get(bufID) == null)
			return false;
		Map<String, WriteConnection> serverConnMap = wConnMap.get(bufID);
		if(serverConnMap.get(server) == null)
			return false;
		
		WriteConnection wConn = serverConnMap.get(server);
		try {
			//try to provision some resource.
			if( !wConn.provision(amount))
			{
				System.err.println("Unable to provision resource from " + wConn.getServer());
				return false;
			}
			System.out.println("Successfully provision from " + wConn.getServer());
		} 
		catch (TException e) {
			// TODO Auto-generated catch block
			System.err.println("Provision call failure to " + wConn.getServer() +"\n" + e.getMessage());
			e.printStackTrace();
			return false;
		}
		return true;
	}
	public List<DataMsg> write(List<DataMsg> data, String server, int bufID) throws TException
	{
		Map<String, WriteConnection> serverConnMap = wConnMap.get(bufID);
		if(serverConnMap == null)
			return data;
		WriteConnection wConn = serverConnMap.get(server);
		if(wConn == null)
			return data;
		
		return wConn.write(data);
	}
	
	public int getBufUsage(int bufID)
	{
		int usage = 0;
		
		Map<String, WriteConnection> serverConnMap = wConnMap.get(bufID);
		if(serverConnMap == null)
			return 0;
		
		Iterator<WriteConnection> it = serverConnMap.values().iterator();
		synchronized(serverConnMap)
		{
			while(it.hasNext())
			{
				WriteConnection wConn = it.next();
				usage += getBufServerUsage(wConn.getServer(), bufID);
			}
		}
		return usage;
	}
	
	public int getBufServerUsage(String server, int bufID)
	{
		return Math.abs(getBufServerRes(server, bufID));
	}
	
	public int getBufServerRes(String server, int bufID)
	{
		Map<String, WriteConnection> bufConn = wConnMap.get(bufID);
		if(bufConn == null)
			return 0;
		
		WriteConnection wConn = bufConn.get(server);
		if(wConn == null)
			return 0;
		
		return wConn.getResource();
	}
	
}
